package com.funtl.spring.cloud.alibaba.unit.controller;

import com.alibaba.fastjson.JSONObject;
import com.funtl.spring.cloud.alibaba.unit.domain.News;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.elasticsearch.search.suggest.completion.CompletionSuggestionBuilder;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.ResultsExtractor;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @ClassName NewsController
 * @Description TODO
 * @Author Mr.Starry
 * @Date 2020/1/1 14:39
 * @Version 1.0
 */
@RestController
@RequestMapping("/new")
@CrossOrigin(origins = "*")
public class NewsController {
    @Resource
    private ElasticsearchTemplate elasticsearchTemplate;


    /*
     GET news/_search
     {
       "_source": ["id", "title", "url", "content"],
       "query": {
         "multi_match": {
           "query": "李小璐离婚",
           "fields": ["title", "content"]
         }
     },
       "highlight": {
         "pre_tags": "<font color='red'>",
         "post_tags": "</font>",
         "fields": {
           "title": {},
           "content": {}
         }
       }
     }
     */
    @RequestMapping("/search")
    public Object executeSearch(String text) {

        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withIndices("news")
                .withSourceFilter(new FetchSourceFilter(new String[]{"id", "title", "url", "content"}, new String[]{}))
                .withQuery(new MultiMatchQueryBuilder(text, "title", "content"))
                .withHighlightBuilder(
                        new HighlightBuilder()
                                .preTags("<font color='red'>")
                                .postTags("</font>")
                                .field("title")
                                .field("content")
                ).build();


        List<News> list = elasticsearchTemplate.query(searchQuery, new MyResultsExtractor());

        return list;
    }


    public class MyResultsExtractor implements ResultsExtractor<List<News>> {

        /**
         * 将高亮的title设置到 News的title中，
         * 将高亮的content设置到News的content中
         */
        @Override
        public List<News> extract(SearchResponse response) {

            List<News> newsList = new ArrayList<>();

            SearchHits searchHits = response.getHits();  //获取命中大的hits

            SearchHit[] searchHitsArray = searchHits.getHits(); //获取具体的数据

            for(SearchHit searchHit : searchHitsArray) {

                //System.out.println(JSONObject.toJSONString(searchHit));
                // 获取 _source 的数据， 但是得到是一个json类型的字符串
                String newJsonStr = searchHit.getSourceAsString(); //获取到News对象对应JSON字符串

                // 将json字符串转换为News类型的对象
                News news = JSONObject.parseObject(newJsonStr, News.class);


                // 获取高亮的数据
                Map<String, HighlightField> map  = searchHit.getHighlightFields();

                HighlightField titleField = map.get("title");  // 获取title的高亮文本数据

                // 有些高亮数据 title中可能没有
                StringBuffer fieldStringBuffer = new StringBuffer();
                if(null != titleField) {
                    Text[] texts = titleField.getFragments();  //获取高亮碎片
                    for(Text text : texts) {
                        fieldStringBuffer.append(text.toString());
                    }
                    news.setTitle(fieldStringBuffer.toString());
                }

                HighlightField contentField = map.get("content");

                // 有些高亮数据 title中可能没有
                StringBuffer contentStringBuffer = new StringBuffer();

                if(null != contentField) {
                    Text[] texts = contentField.getFragments();  //获取高亮碎片
                    for(Text text : texts) {
                        contentStringBuffer.append(text.toString());
                    }
                    news.setContent(contentStringBuffer.toString());
                }
                newsList.add(news);
            }

            return newsList;
        }
    }

    /*
     GET news/_search
     {
       "_source": ["id"],
       "suggest": {
         "tags_suggest": {
           "prefix": "wy",
           "completion": {
             "field": "tags",
             "skip_duplicates": true
          }
       },
         "title_suggest": {
           "text": "lxl",
           "term": {
             "field": "title",
             "suggest_mode": "missing"
            }
          }
        }
     }
     */
    @RequestMapping("/suggest")
    public Object suggestSearch(String prefix) {
        CompletionSuggestionBuilder completionSuggestion = new CompletionSuggestionBuilder("tags")
                .prefix(prefix)
                .skipDuplicates(true);  // 忽略掉重复复

//        TermSuggestionBuilder termSuggestionBuilder = new TermSuggestionBuilder("title")
//                .text("lxl")
//                .suggestMode(TermSuggestionBuilder.SuggestMode.ALWAYS);


        SuggestBuilder suggestBuilder = new SuggestBuilder()
                .addSuggestion("tags_suggest", completionSuggestion);
//                .addSuggestion("title_suggest", termSuggestionBuilder);


        SearchRequestBuilder searchRequestBuilder =  elasticsearchTemplate.getClient()
                .prepareSearch("news")  //指定索引名
                .suggest(suggestBuilder)   // suggest接收一个SuggestBuilder
                .setFetchSource(new String[]{"id"}, new String[]{});

        SearchResponse response = searchRequestBuilder.get();  // get() 执行具体的查询

        Suggest suggest = response.getSuggest();

        Suggest.Suggestion suggestion = suggest.getSuggestion("tags_suggest"); //获取对应的建议

        // 对于term的suggest来讲，如果有多个单词，会有多个推荐，在completion中不会出现这个情况，
        // 但是底层会了实现通用性，所以都用List来进行封装
        List<?> list = suggestion.getEntries();  //

        /**
         * 因为Vue的自动提示功能需要的数据格式为： [{"value": ""}, {"value":""}]
         */
        List<Map<String, String>> results = new ArrayList<>();

        list.forEach(obj -> {
            // 遍历集合的每个对象的真实类型是CompletionSuggestion.Entrry
            // 如果是 CompletionSuggestionBuilder, 那么集合中的每个对象属于 TermSuggestion.Entry
            // 如果是 PhraseSuggestionBuilder, 那么集合中的每个对象属于 PhraseSuggestion.Entry
            if(obj instanceof CompletionSuggestion.Entry) {
                CompletionSuggestion.Entry entry = (CompletionSuggestion.Entry)obj;

                // 获取option的目的是获取text。
                List<CompletionSuggestion.Entry.Option> options = entry.getOptions();

                options.forEach(o -> {
                    Text text = o.getText();
                    String suggestText = text.toString();

                    Map<String, String> map = new HashMap<>();
                    map.put("value", suggestText);
                    results.add(map);
                });
            }
        });
        return results;
    }
}
